package topic

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"mall/common/globalkey"
	"mall/common/modelx"
	"mall/common/resultx"
	"mall/service/backend/common/utils/pointy"
	"mall/service/forum/model"
	"mall/service/forum/rpc/internal/svc"
	"mall/service/forum/rpc/types/forum"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetTopicByIdLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewGetTopicByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTopicByIdLogic {
	return &GetTopicByIdLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *GetTopicByIdLogic) GetTopicById(in *forum.BaseIdReq) (*forum.TopicInfo, error) {
	if (in.Filter == nil || len(*in.Filter) == 0) && (in.Id == nil || *in.Id == 0) {
		return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
	}

	preloadMap, eagerLoadMap := modelx.ParsePreloadAndEagerLoad(in.Preload, in.EagerLoad)

	logrus.Info(fmt.Sprintf("GetTopicById preloadMap: %+v", preloadMap))

	logrus.Info(fmt.Sprintf("GetTopicById eagerLoadMap: %+v", eagerLoadMap))

	forumTopicEntityName := model.RpcEntityNames.ForumTopic
	_, hasSettingMap := model.RpcEntityPreloadMap[forumTopicEntityName]
	if !hasSettingMap {
		return &forum.TopicInfo{}, nil
	}

	forumTopicRepositoryName := model.RepositoryNames.ForumTopic
	columnMap, hasColumnMap := model.RepositoryPreloadMap[forumTopicRepositoryName]
	if !hasColumnMap {
		return &forum.TopicInfo{}, nil
	}

	var topic *model.ForumTopic
	if in.Id != nil && *in.Id > 0 {
		data, err := l.svcCtx.Topic.FindOne(l.ctx, *in.Id)
		if err != nil {
			return nil, err
		}

		topic = data
	} else {
		var selectCols []string
		if preloadFields, hasPreloadFields := preloadMap[forumTopicEntityName]; hasPreloadFields {
			selectCols = modelx.ParseSelectColumns(preloadFields, columnMap)
		}

		filterMap := modelx.ParseFilter(in.Filter, columnMap)
		logrus.Info(fmt.Sprintf("GetTopicById filterMap: %+v", filterMap))
		if filterMap == nil {
			return nil, resultx.StatusError(resultx.REUQEST_PARAM_ERROR, "")
		}

		orderBy := modelx.ParseSorter(in.Sorter, columnMap)
		logrus.Info(fmt.Sprintf("GetTopicById orderBy: %s", orderBy))
		if len(orderBy) == 0 {
			orderBy = model.ForumTopicColumns.TopicModifiedDate + " DESC"
		}

		res, _, err := l.svcCtx.Topic.GetList(l.ctx, model.M{
			"pageSize": uint64(1),
			"orderBy":  orderBy,
			"select":   strings.Join(selectCols, ","),
		}, -1, filterMap)
		if err != nil {
			return nil, err
		}

		if len(res) == 0 {
			return &forum.TopicInfo{}, nil
		}

		topic = res[0]
	}

	if topic == nil {
		return &forum.TopicInfo{}, nil
	}

	if topic.TopicStatus == uint8(globalkey.StatusDeleted) {
		return &forum.TopicInfo{}, nil
	}

	if in.VisitorId != nil && *in.VisitorId != topic.TopicAuthorID && topic.TopicStatus != uint8(globalkey.StatusApproved) {
		return &forum.TopicInfo{}, nil
	}

	tid := topic.TopicID
	var posts []*forum.PostInfo
	var media []*forum.ForumMediaInfo
	var meta []*forum.ForumMetaInfo
	subEntitySettingMap := make(map[string]map[string]*model.ColumnSetting)
	if eagerLoadEntities, hasEagerLoadEntities := eagerLoadMap[forumTopicEntityName]; hasEagerLoadEntities {
		for _, entity := range eagerLoadEntities {
			if entitySettingMap, hasEntitySettingMap := model.RpcEntityPreloadMap[entity]; hasEntitySettingMap {
				subEntitySettingMap[entity] = entitySettingMap
			}
		}
	}

	forumPostEntityName := model.RpcEntityNames.ForumPost
	forumPostRepositoryName := model.RepositoryNames.ForumPost
	if _, hasEntitySettingMap := subEntitySettingMap[forumPostEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[forumPostEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[forumPostRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			postFilterMap := make(map[string]map[string][]any)
			postFilterMap[model.ForumPostColumns.TopicID] = map[string][]any{
				"equalTo": []any{tid},
			}
			postFilterMap[model.ForumPostColumns.PostStatus] = map[string][]any{
				"equalTo": []any{globalkey.StatusApproved},
			}
			var orderBy string = model.ForumPostColumns.PostModifiedDate + " DESC"
			postList, _, err := l.svcCtx.Topic.GetReplyList(l.ctx, model.M{
				"pageSize": uint64(10),
				"orderBy":  orderBy,
				"select":   strings.Join(subSelectCols, ","),
			}, -1, postFilterMap)
			if err != nil {
				return nil, err
			}

			if len(postList) > 0 {
				for _, v := range postList {
					posts = append(posts, &forum.PostInfo{
						Id:            pointy.GetPointer(v.PostID),
						ForumId:       pointy.GetPointer(v.ForumID),
						AuthorId:      pointy.GetPointer(v.PostAuthorID),
						TopicId:       pointy.GetPointer(v.TopicID),
						ParentId:      pointy.GetPointer(v.ParentID),
						ContentHtml:   pointy.GetPointer(v.PostContent),
						Status:        pointy.GetPointer(uint32(v.PostStatus)),
						Sticky:        pointy.GetPointer(uint32(v.Sticky)),
						CommentStatus: pointy.GetPointer(uint32(v.CommentStatus)),
						CommentCount:  pointy.GetPointer(uint32(v.CommentCount)),
						PhotoCount:    pointy.GetPointer(uint32(v.PhotoCount)),
						CreatedAt:     pointy.GetPointer(modelx.FormatTime(v.PostDate, "")),
						UpdatedAt:     pointy.GetPointer(modelx.FormatTime(v.PostModifiedDate, "")),
						Author: &forum.ForumAuthorInfo{
							Id:        pointy.GetPointer(v.PostAuthorID),
							Name:      pointy.GetPointer(v.PostAuthorNickname),
							Email:     pointy.GetPointer(v.PostAuthorEmail),
							Avatar:    pointy.GetPointer(v.PostAuthorAvatar),
							Ip:        pointy.GetPointer(v.PostAuthorIP),
							Level:     pointy.GetPointer(uint32(v.PostAuthorRankID)),
							Anonymous: pointy.GetPointer(uint32(v.PostAuthorAnonymous)),
						},
					})
				}
			}
		}
	}

	forumMediaEntityName := model.RpcEntityNames.ForumMedia
	forumMediaRepositoryName := model.RepositoryNames.ForumMedia
	if _, hasEntitySettingMap := subEntitySettingMap[forumMediaEntityName]; hasEntitySettingMap {
		subPreloadFields, hasSubPreloadFields := preloadMap[forumMediaEntityName]
		subColumnMap, hasSubColumnMap := model.RepositoryPreloadMap[forumMediaRepositoryName]
		if hasSubPreloadFields && hasSubColumnMap {
			var subSelectCols []string = modelx.ParseSelectColumns(subPreloadFields, subColumnMap)

			mediaFilterMap := make(map[string]map[string][]any)
			mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectID] = map[string][]any{
				"equalTo": []any{tid},
			}
			mediaFilterMap[model.ForumAttachmentRelationshipColumns.ObjectType] = map[string][]any{
				"equalTo": []any{globalkey.AttachmentLogTypeForumTopic},
			}
			var orderBy string = model.ForumAttachmentRelationshipColumns.AttachmentThumbnail + " DESC," +
				model.ForumAttachmentRelationshipColumns.AttachmentOrder
			mediaList, _, err := l.svcCtx.Post.GetMedia(l.ctx, model.M{
				"orderBy": orderBy,
				"select":  strings.Join(subSelectCols, ","),
			}, -1, mediaFilterMap)
			if err != nil {
				return nil, err
			}

			if len(mediaList) > 0 {
				for _, v := range mediaList {
					media = append(media, &forum.ForumMediaInfo{
						Uid:       pointy.GetPointer(v.AttachmentRelationshipID),
						Id:        pointy.GetPointer(strconv.FormatInt(int64(v.AttachmentID), 10)),
						Uri:       pointy.GetPointer(v.AttachmentSource),
						Url:       pointy.GetPointer(v.AttachmentImageURL),
						Thumbnail: pointy.GetPointer(uint32(v.AttachmentThumbnail)),
						Order:     pointy.GetPointer(uint32(v.AttachmentOrder)),
						AltText:   pointy.GetPointer(v.AttachmentImageAlt),
						Meta:      pointy.GetPointer(v.AttachmentImageMeta),
					})
				}
			}
		}
	}

	forumPostMetaEntityName := model.RpcEntityNames.ForumTopicMeta
	if entitySettingMap, hasEntitySettingMap := subEntitySettingMap[forumPostMetaEntityName]; hasEntitySettingMap {
		if subPreloadFields, hasSubPreloadFields := preloadMap[forumPostMetaEntityName]; hasSubPreloadFields {
			var metaKeys []any
			metaKeyMap := make(map[string]string)
			for _, field := range subPreloadFields {
				fieldSetting, hasFieldSetting := entitySettingMap[field]
				if hasFieldSetting && len(fieldSetting.Name) > 0 {
					metaKeys = append(metaKeys, fieldSetting.Name)
					metaKeyMap[fieldSetting.Name] = field
				}
			}
			if len(metaKeys) > 0 {
				metaFilterMap := make(map[string]map[string][]any)
				metaFilterMap[model.ForumTopicMetumColumns.TopicID] = map[string][]any{
					"equalTo": []any{tid},
				}
				metaFilterMap[model.ForumTopicMetumColumns.MetaKey] = map[string][]any{
					"in": metaKeys,
				}
				metaList, _, err := l.svcCtx.Post.GetMeta(l.ctx, model.M{}, -1, metaFilterMap)
				if err != nil && err != model.ErrNotFound {
					return nil, err
				}

				if len(metaList) > 0 {
					for _, v := range metaList {
						key := v.MetaKey
						if len(key) > 0 && v.MetaValue.Valid {
							val := v.MetaValue.String
							if field, hasField := metaKeyMap[key]; hasField {
								meta = append(meta, &forum.ForumMetaInfo{
									Id:    pointy.GetPointer(v.MetaID),
									Key:   pointy.GetPointer(field),
									Value: pointy.GetPointer(val),
								})
							}
						}
					}
				}
			}
		}
	}

	if in.SearchLogId != nil {
		var visitorId uint64
		if in.VisitorId != nil {
			visitorId = *in.VisitorId
		}
		var platformId uint
		if in.PlatformId != nil {
			platformId = uint(*in.PlatformId)
		}
		l.svcCtx.ForumUser.InsertClickLog(l.ctx, &model.ForumUserClickLog{
			UserID:      visitorId,
			PlatformID:  platformId,
			SearchLogID: *in.SearchLogId,
			ObjectID:    tid,
			ObjectType:  uint16(globalkey.AttachmentLogTypeForumTopic),
			CreatedAt:   time.Now(),
		})
	}

	return &forum.TopicInfo{
		Id:        pointy.GetPointer(tid),
		ForumId:   pointy.GetPointer(topic.ForumID),
		AuthorId:  pointy.GetPointer(topic.TopicAuthorID),
		Title:     pointy.GetPointer(topic.Title),
		Slug:      pointy.GetPointer(topic.Slug),
		Status:    pointy.GetPointer(uint32(topic.TopicStatus)),
		Sticky:    pointy.GetPointer(uint32(topic.Sticky)),
		PostCount: pointy.GetPointer(uint32(topic.PostCount)),
		CreatedAt: pointy.GetPointer(modelx.FormatTime(topic.TopicDate, "")),
		UpdatedAt: pointy.GetPointer(modelx.FormatTime(topic.TopicModifiedDate, "")),
		Author: &forum.ForumAuthorInfo{
			Id:        pointy.GetPointer(topic.TopicAuthorID),
			Name:      pointy.GetPointer(topic.TopicAuthorNickname),
			Email:     pointy.GetPointer(topic.TopicAuthorEmail),
			Avatar:    pointy.GetPointer(topic.TopicAuthorAvatar),
			Ip:        pointy.GetPointer(topic.TopicAuthorIP),
			Level:     pointy.GetPointer(uint32(topic.TopicAuthorRankID)),
			Anonymous: pointy.GetPointer(uint32(topic.TopicAuthorAnonymous)),
		},
		Posts: posts,
		Media: media,
		Meta:  meta,
	}, nil
}
